package com.mryin.config;

import com.mryin.Realm.UserRealm;
import com.mryin.shiro.ShiroLoginFilter;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.crazycake.shiro.IRedisManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.DelegatingFilterProxy;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

import javax.servlet.Filter;
import java.util.*;

/**
 * @author: Mr.Yin
 * @create: 2021-02-19 22:47
 **/
@Configuration
@EnableConfigurationProperties(ShiroProperties.class)
public class ShiroConfig {


    @Autowired
    private RedisProperties redisProperties;

    @Autowired
    private ShiroProperties shiroProperties;

    /**
     * 配置过滤器 Shiro 的Web过滤器 id必须和web.xml里面的shiroFilter的 targetBeanName的值一样
     */
    @Bean("shiroFilter")
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        //注入安全管理器
        bean.setSecurityManager(securityManager);
        //注入登陆页面
        bean.setLoginUrl(shiroProperties.getLoginUrl());
        //注入未授权的页面地址
        bean.setUnauthorizedUrl(shiroProperties.getUnauthorizedUrl());

        // 创建自定义filter
        Map<String, Filter> map = new HashMap<>();
        // 注入过滤器
        map.put("authc", new ShiroLoginFilter());
        bean.setFilters(map);

        //注入过滤器
        Map<String, String> filterChainDefinition = new HashMap<>();

        //注入放行地址
        if (shiroProperties.getAnonUrls() != null && shiroProperties.getAnonUrls().length > 0) {
            String[] anonUrls = shiroProperties.getAnonUrls();
            for (String anonUrl : anonUrls) {
                System.out.println(anonUrl);
            }
            for (String anonUrl : anonUrls) {
                filterChainDefinition.put(anonUrl, "anon");
            }
        }
        //注入登出的地址
        if (shiroProperties.getLogoutUrl() != null) {
            filterChainDefinition.put(shiroProperties.getLogoutUrl(), "logout");
        }
        //filterChainDefinition.put("/api/employees/AddEmployee","perms[User:Add]");
        //filterChainDefinition.put("/api/employees/DeleteEmployee", "perms[User:Delete]");
        //filterChainDefinition.put("/api/employees/UpdateEmployee","perms[User:Update]");
        filterChainDefinition.put("/api/employees/employee", "perms[User:List]");

        //注入拦截的地址
        String[] authcUrls = shiroProperties.getAuthcUrls();
        if (authcUrls != null && authcUrls.length > 0) {
            for (String authcUrl : authcUrls) {
                filterChainDefinition.put(authcUrl, "authc");
            }
        }
        bean.setFilterChainDefinitionMap(filterChainDefinition);
        return bean;
    }


    /**
     * 注册过滤器
     *
     * @return
     */
    @Bean
    public FilterRegistrationBean<DelegatingFilterProxy> filterRegistrationBeanDelegatingFilterProxy() {
        FilterRegistrationBean<DelegatingFilterProxy> bean = new FilterRegistrationBean<>();
        //创建过滤器
        DelegatingFilterProxy proxy = new DelegatingFilterProxy();
        proxy.setTargetBeanName("shiroFilter");
        proxy.setTargetFilterLifecycle(true);
        bean.setFilter(proxy);
        List<String> servletNames = new ArrayList<>();
        // 注入前端管理器
        servletNames.add(DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
        bean.setServletNames(servletNames);
        return bean;
    }

    /**
     * 声明安全管理器
     * 解决session共享问题
     * shiro redis
     *
     * @param defaultWebSessionManager 关键点  里面解决 token问题
     * @param redisSession             关键点
     * @param userRealm                认证
     * @return
     */
    @Bean("securityManager")
    public SecurityManager securityManager(DefaultWebSessionManager defaultWebSessionManager, SessionDAO redisSession, @Qualifier("userRealm") UserRealm userRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 设置了一个redisSession   前后端分离 存储 token
        defaultWebSessionManager.setSessionDAO(redisSession);
        //  改变了会话   注入到安全管理当中  前后端分离    自定义session管理 使用redis
        securityManager.setSessionManager(defaultWebSessionManager);

        // 注入realm
        securityManager.setRealm(userRealm);
        return securityManager;
    }

    @Bean("userRealm")
    public UserRealm getEmployeesRealm() {
        return new UserRealm();
    }


    /**
     * cacheManager 缓存 redis实现
     * <p>
     * 使用的是shiro-redis开源插件
     *
     * @return
     */
    @Bean
    public RedisCacheManager cacheManager() {
        RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setRedisManager(redisManager());
        return redisCacheManager;
    }


    /**
     * 开启shiro aop注解支持.
     * 使用代理方式;所以需要开启代码支持;
     *
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

    /**
     * 加入该注解 授权等注解启动
     *
     * @return
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }

    /**
     * 使用Redis  来存储登录的信息
     * sessionDao 还需要设置给sessionManager
     * 前后端分离存储 token
     * <p>
     * IRedisManager redisManager  为shrio-redis依赖的 接口对象
     */

    @Bean
    public SessionDAO redisSessionDAO(@Qualifier("redisManager") IRedisManager redisManager) {
        // 创建redisSession   这里面 应该就是 存储 shiro-session 的token了吧
        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
        //操作那个redis
        redisSessionDAO.setRedisManager(redisManager);
        // 用户的登录信息保存多久？ 7 天
        //redisSessionDAO.setExpire(7 * 24 * 3600);
        //redisSessionDAO.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        //redisSessionDAO.setKeySerializer(keySerializer);
        //redisSessionDAO.setValueSerializer(valueSerializer);
        System.out.println("TOKEN缓存到Redis当中==============================================================");
        System.out.println(redisSessionDAO.getKeySerializer());
        System.out.println(redisSessionDAO.getValueSerializer());
        System.out.println(redisSessionDAO.getSessionInMemoryEnabled());
        System.out.println(redisSessionDAO.getSessionInMemoryTimeout());
        return redisSessionDAO;
    }


    /**
     * redis 数据库  连接池
     *
     * @return
     */
    @Bean(name = "redisManager")
    public IRedisManager redisManager() {
        RedisManager redisManager = new RedisManager();
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        // 链接池的最量 20 ，并发特别大时，连接池的数据可以最大增加20个
        jedisPoolConfig.setMaxTotal(redisProperties.getJedis().getPool().getMaxActive());
        // 连接池的最大剩余量15个 ：并发不大，池里面的对象用不上，里面对象太多了。浪费空间
        jedisPoolConfig.setMaxIdle(redisProperties.getJedis().getPool().getMaxIdle());
        // 连接池初始就有10 个
        jedisPoolConfig.setMinIdle(redisProperties.getJedis().getPool().getMinIdle());
        // 创建连接池
        JedisPool jedisPool = new JedisPool(jedisPoolConfig, redisProperties.getHost(), redisProperties.getPort(), 2000, redisProperties.getPassword());
        // 设置连接池
        redisManager.setJedisPool(jedisPool);
        return redisManager;
    }
}
